# -*- coding: utf-8 -*-
import base64
import json
import time
from collections import OrderedDict

import requests
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from django.conf import settings

from async import async_job
from common.cache import redis_cache
from common.channel.pay import check_channel_order
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {
    '376a084b198252c9816f3f48f48774f1': {
        # 大吉支付  支付宝wap  10-10000  2.5%  DWC
        # 大吉支付  微信扫码  50-5000 3.9% DWC
        'gateway': 'http://pay.shunley.net/gateway.do',
        'query_gateway': 'http://pay.shunley.net/query.do',
        'pub_key': """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDsRUBmzeL3FBQIjLU23elSuspw
U2EWPhG3tbqofOD+3VQ6ZEIDX43xusrCdltc4cjff/hItzth1h6tQ+qbGt9Rg4nJ
Rb6ISRRK0WVjX8QPRzmIEBJybryvG0aGOTPVJeIGfdyicOY3CVOkWNWC+zxoZW5W
iJJIrFuczOVC+nv6jwIDAQAB
-----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANQXUvgrIL5Sa3AV
NwQetGOGyuefQTjeuw3om6tABed0BEgJ899X9ekVW3ODDUFN8oAK7rDbykpodccQ
zaLmVWQrowlQsEv/AzyycVfrnAClc/2tzeKRbPaZYNJ8XIDk6li8bnMx4VqQEPmE
p3bPEzyxLsF6f2Oc3VA4RrZC++j9AgMBAAECgYBAw06GkUTSWQCNnSzLiUS0D3jt
+WdFIl4kBR9jz4z9/CNCjCd3z7g7kFX4k8/8XPzrtGrmfnbq+bLdMnhYgdUYztuA
MXfjGaI4rOxqtoqdBlu2Iu9LT6Ywoo/yswPO+9dOri3ljpIdGV/Z+SF1k0WZK2JS
YWMk4APQk3LsAcEt0QJBAPdQtgF/hQVUoB6FJGdnsTvqragWL0fJ2oVdRv7eoDJW
bEIllrMEKUpQGkpMmdei/MsfBOBudXmZr/Jxmw5WZAsCQQDbifVmujXxK8saBf7G
Mh2PmM+Am6F+hulpNAmIkDjQyiGFbqCsNEluUYkzJbb9VWFMtq34m4TXk3FPDKHF
skQXAkBXKhEbsg/18v+EAjT0ZO1oSqvtw/Ph/1iPDHMLPPeFBkSdg5X7fgn3cQfz
C9Bk2cLy6/nSB9xLgUkrz7JweS5FAkA5VEtrKhFQC1mcUBh59ThW+VamhP2lJuHp
ARfEO7U6gK5EQ+FdVsIeNtiXLLAec59XAxVnKGWGThB2zhuonqa9AkAzL/RcB2Wu
osRQYVX8N+rzK2pyViUoZIUfei83ERqpeNO/ZbDFtwcFBYuSfIpDrMLOUQqT0Pvb
7rAtxwBaKEoo
-----END PRIVATE KEY-----""",
    },
}


def _get_gateway(mch_id):
    return APP_CONF[mch_id]['gateway']


def _get_query_gateway(mch_id):
    return APP_CONF[mch_id]['query_gateway']


def _get_pri_key(mch_id):
    return APP_CONF[mch_id]['pri_key']


def _get_pub_key(mch_id):
    return APP_CONF[mch_id]['pub_key']


def _gen_sign(message, app_id):
    key = RSA.importKey(_get_pri_key(app_id))
    h = SHA.new(message)
    signer = Signature_pkcs1_v1_5.new(key)
    signature = signer.sign(h)
    return base64.b64encode(signature)


def _gen_pub_sign(message, app_id):
    key = RSA.importKey(_get_pub_key(app_id))
    h = SHA.new(message)
    signer = Signature_pkcs1_v1_5.new(key)
    signature = signer.sign(h)
    return base64.b64encode(signature)


def verify(message, signature, key):
    pub_key = RSA.importKey(key)
    decode_message = base64.b64decode(message)
    h = SHA.new(decode_message)
    verify_result = Signature_pkcs1_v1_5.new(pub_key).verify(h, signature)
    _LOGGER.info("dajipay verify_result is: %s, 0 mean success", verify_result)
    return verify_result


def generate_sign(parameter):
    s = ''
    for k in sorted(parameter.keys()):
        if k != 'sign' and parameter[k]:
            s += '%s=%s&' % (k, parameter[k])
    _LOGGER.info("dajipay sign is: %s", s[:len(s) - 1])
    return s[:len(s) - 1]


def verify_notify_sign(params, app_id):
    sign = params['sign']
    params.pop('sign')
    params_to_sign = generate_sign(params)
    if verify(params_to_sign, sign, _get_pub_key(app_id)) != 0:
        raise ParamError('dajipay sign not pass, data: %s' % params)


def _get_pay_type(service):
    if service == 'alipay':
        return 'alipay'
    elif service == 'alipay_scan':
        return 'alipay_scan'
    elif service == 'wechatscan':
        return 'wxpay_scan'
    return 'alipay'


def _build_form(params, gateway):
    html = u"<head><title>loading...</title></head><form id='submit' name='submit' action='" + gateway + "' method='post'>"
    for k, v in params.items():
        html += "<input type='hidden' name='%s' value='%s'/>" % (k, v)
    html += "</form>"
    html += "<script>doc" \
            "ument.forms['submit'].submit();</script>"
    return html


def rsa_long_encrypt(pub_key_str, msg, length=117):
    """
    单次加密串的长度最大为 (key_size/8)-11
    1024bit的证书用100， 2048bit的证书用 200
    """
    pubobj = RSA.importKey(pub_key_str)
    pubobj = Cipher_pkcs1_v1_5.new(pubobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(pubobj.encrypt(msg[i:i + length]))
    return "".join(res)


def rsa_long_decrypt(priv_key_str, msg, length=128):
    """
    1024bit的证书用128，2048bit证书用256位
    """
    privobj = RSA.importKey(priv_key_str)
    privobj = Cipher_pkcs1_v1_5.new(privobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(privobj.decrypt(msg[i:i + length], ''))
    return "".join(res)


def create_charge(pay, pay_amount, info):
    service = info.get('service')
    app_id = info['app_id']
    parameter_dict = OrderedDict((
        ('version', '1.0.0'),
        ('token', app_id),
        ('timestamp', int(time.time())),
        ('amount', '%.2f' % pay_amount),
        ('product_name', 'DaJiPay'),
        ('paycode', _get_pay_type(service)),
        ('out_trade_no', str(pay.id)),
        ('attach', 'Charge'),
        ('front_url', '{}/pay/api/{}/dajipay/{}'.format(settings.NOTIFY_PREFIX, settings.RETURN_PATH, pay.id)),
        ('asynch_url', '{}/pay/api/{}/dajipay/{}'.format(settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
    ))
    sign_str = generate_sign(parameter_dict)
    parameter_dict['sign'] = _gen_sign(sign_str, app_id)
    data = json.dumps(parameter_dict, separators=(',', ':'))
    _LOGGER.info("dajipay create_charge data: %s, order_id: %s", data, parameter_dict['out_trade_no'])
    public_encrypted_data = rsa_long_encrypt(_get_pub_key(app_id), data, 117)
    request_data = base64.b64encode(public_encrypted_data)
    html_text = _build_form({"data": request_data}, _get_gateway(app_id))
    cache_id = redis_cache.save_html(pay.id, html_text)
    _LOGGER.info('dajipay url: %s', settings.PAY_CACHE_URL + cache_id)
    return {'charge_info': settings.PAY_CACHE_URL + cache_id}


# SUCCESS
def check_notify_sign(request, app_id):
    _LOGGER.info("dajipay notify request.body: %s", request.body)
    message = base64.decodestring(request.body)
    tmp = rsa_long_decrypt(_get_pri_key(app_id), message)
    data = json.loads(tmp)
    _LOGGER.info("dajipay notify request data: %s, order_id: %s", data, data['out_trade_no'])
    verify_notify_sign(data, app_id)
    pay_id = data['out_trade_no']
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s" % request.body)
        raise ParamError('dajipay event does not contain pay ID')

    pay = order_db.get_pay(str(pay_id))
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id
    trade_status = 'success'
    total_fee = float(data['amount'])
    trade_no = data['out_trade_no']
    extend = {
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee
    }
    check_channel_order(pay_id, total_fee, app_id)
    # 订单状态，1-正常，2-退单，3-交易关闭
    if trade_status == 'success' and total_fee > 0.0:
        _LOGGER.info('dajipay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, data['orderid'], extend)
        # async notify
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    ''' 查询订单 '''
    pay_id = pay_order.id
    # third_id = pay_order.third_id
    parameter_dict = OrderedDict((
        ('version', '1.0.0'),
        ('token', app_id),
        ('timestamp', int(time.time())),
        ('out_trade_no', str(pay_id)),
    ))
    sign_str = generate_sign(parameter_dict)
    parameter_dict['sign'] = _gen_sign(sign_str, app_id)
    _LOGGER.info("dajipay query req data: %s", json.dumps(parameter_dict))
    data = json.dumps(parameter_dict, separators=(',', ':'))
    public_encrypted_data = rsa_long_encrypt(_get_pub_key(app_id), data, 117)
    request_data = base64.b64encode(public_encrypted_data)

    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_query_gateway(app_id), data={"data": request_data}, headers=headers, timeout=5)
    _LOGGER.info("dajipay query rsp code: %s, data: %s, order_id: %s", response.status_code, response.text,
                 json.loads(response.text)['data']['out_trade_no'])

    if response.status_code == 200:
        query_data = json.loads(response.text)['data']
        _LOGGER.info("resp_data is: %s", query_data)
        query_data['sign'] = json.loads(response.text)['sign']
        verify_notify_sign(query_data, app_id)

        mch_id = pay_order.mch_id
        trade_status = str(query_data['pay_status'])
        total_fee = float(query_data['amount'])
        trade_no = query_data['out_trade_no']
        extend = {
            'trade_status': trade_status,
            'total_fee': total_fee,
            'trade_no': trade_no,
        }
        if trade_status == '1':
            check_channel_order(pay_id, total_fee, app_id)

            _LOGGER.info('dajipay query order success, mch_id:%s pay_id:%s' % (mch_id, pay_order.id))
            order_db.add_pay_success(pay_order.mch_id, pay_order.id,
                                     total_fee, pay_order.third_id, extend)
            async_job.notify_mch(pay_order.id)
